home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume8 / make.gy / part01 next >
Encoding:
Text File  |  1989-11-02  |  36.8 KB  |  1,605 lines

  1. Newsgroups: comp.sources.misc
  2. subject: v08i104: Make Version 1.5 (Part 1 of 3)
  3. From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  4. Reply-To: greggy@etude.UUCP (Greg Yachuk)
  5.  
  6. Posting-number: Volume 8, Issue 104
  7. Submitted-by: greggy@etude.UUCP (Greg Yachuk)
  8. Archive-name: make.gy/part01
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then unpack
  12. # it by saving it into a file and typing "sh file".  To overwrite existing
  13. # files, type "sh file -c".  You can also feed this as standard input via
  14. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  15. # will see the following message at the end:
  16. #        "End of archive 1 (of 3)."
  17. # Contents:  MANIFEST README build.c make.c tstring.c
  18. # Wrapped by greggy@etude on Mon Oct 30 19:10:07 1989
  19. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  20. if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  21.   echo shar: Will not clobber existing file \"'MANIFEST'\"
  22. else
  23. echo shar: Extracting \"'MANIFEST'\" \(548 characters\)
  24. sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
  25. X   File Name        Archive #    Description
  26. X-----------------------------------------------------------
  27. X MANIFEST                   1    This shipping list
  28. X README                     1    
  29. X build.c                    1    
  30. X decl.h                     3    
  31. X default.dos                3    
  32. X default.mk                 3    
  33. X make.c                     1    
  34. X make.doc                   2    
  35. X make.h                     3    
  36. X makefile                   3    
  37. X makefile.dos               3    
  38. X parse.c                    2    
  39. X tstring.c                  1    
  40. X tstring.h                  3    
  41. END_OF_FILE
  42. if test 548 -ne `wc -c <'MANIFEST'`; then
  43.     echo shar: \"'MANIFEST'\" unpacked with wrong size!
  44. fi
  45. # end of 'MANIFEST'
  46. fi
  47. if test -f 'README' -a "${1}" != "-c" ; then 
  48.   echo shar: Will not clobber existing file \"'README'\"
  49. else
  50. echo shar: Extracting \"'README'\" \(4707 characters\)
  51. sed "s/^X//" >'README' <<'END_OF_FILE'
  52. XMake Version 1.5                    89-10-30
  53. X
  54. XThis is a work-alike program, very close to `make' as it exists on Sun
  55. Xsystems.  It is compiled with Microsoft C (5.1) and runs on PCDOS 3.3.
  56. XIt also compiles and runs on BSD 4.2 Unix.
  57. X
  58. XMany thanks to Kirk Bailey (bailey@mist.cs.orst)
  59. X               Brian Wilson (island!sun!grenada!dr_unix)
  60. X           Jeff Fried (ames!culls!jeff)
  61. X           Arend van den Brug (arend@philmds@philnl@mcvax)
  62. Xfor pointing out problems and improvements.
  63. X
  64. XI have included a makefile and a (rudimentary) default.mk for BSD Unix.
  65. XUpdates, additions and corrections are welcomed.  The shar distribution
  66. Xhas the filenames reversed from the ZOO distribution:
  67. X
  68. X    ZOO distribution        shar distribution
  69. X    ----------------------------    --------------------------
  70. X    makefile    makefile.bsd    makefile.dos    makefile
  71. X    default.mk    default.bsd    default.dos    default.mk
  72. X
  73. X
  74. XThis is because I assume that the ZOO is used on DOS and the shar on
  75. XUnix.  If what you got is not appropriate, rename the appropriate files
  76. Xand make.  For example, if you got the DOS version on a Unix system:
  77. X
  78. X    mv makefile makefile.dos
  79. X    mv makefile.bsd makefile
  80. X    mv default.mk default.dos
  81. X    mv default.bsd default.mk
  82. X    make
  83. X
  84. XAlternatively, you can (probably) just use this following command:
  85. X
  86. X    make -r -f default.bsd -f makefile.bsd
  87. X
  88. XThis is version 1.5.  There is a version 1.4 that was submitted to
  89. Xcomp.binaries.ibm.pc by Arend van den Brug.  I have incorporated many of
  90. Xthe changes into my version and called it 1.5.  These are the changes
  91. Xfrom 1.3 to 1.5:
  92. X     support the -k, -S and -q options (see make.man).
  93. X
  94. X     correctly support the $(MAKE) macro.
  95. X
  96. X     allow target lines to end with a semi-colon and a command.
  97. X
  98. X     corrected bugs when allocated strings are over-run.
  99. X
  100. X     added "*" and "?" to the list of characters that force the use
  101. X     of the shell on Unix
  102. X
  103. X
  104. XThese are the changes from 1.2 to 1.3:
  105. X     don't append shell command lines when encountering multiple
  106. X     targets of the same name.  if this is a *special* target
  107. X     (e.g. .c.obj), just override.  otherwise exit with an error.
  108. X
  109. X     flush output before executing the command, so the output comes
  110. X     out in the right order when redirecting stdout to a file.
  111. X
  112. X     if a file is supposedly built, but does not exists, use current time.
  113. X
  114. X     exit with an error if a target line does not have a ':'.
  115. X
  116. X     don't force a space after "include" in case TAB is used.
  117. X
  118. X     D and F modifiers for Directory and Filename of $@, $<, $*.
  119. X
  120. X     use a dependent file for an implicit rule, if possible.
  121. X
  122. X     allow Makefile as well as makefile, for Unix.
  123. X
  124. X     always print statements when using -n, even if they start with @.
  125. X
  126. X     backquote (`) will force use of a shell, in Unix.
  127. X
  128. XThese are the changes from 1.1 to 1.2:
  129. X     ensure command line macros override makefile macros, even as
  130. X     makefiles are being read in.
  131. X
  132. X     support time checks correctly on MSDOS directories.
  133. X
  134. X     use MAKEFLAGS macro, and set it up for subordinate makes.
  135. X
  136. X     import environment variables and support -e flag.
  137. X
  138. X     handle `-f -' (i.e.  makefile from stdin) correctly.
  139. X
  140. X     clean up some potential NULL pointer dereferences.
  141. X
  142. X     correct errors in handling nested makes.
  143. X
  144. X     modify tokenizing routine to correctly handle trailing separators.
  145. X
  146. X     the documentation has been re-written
  147. X
  148. XThese are the changes from 1.0 to 1.1:
  149. X     modify prerequisite list handling to correctly allow a target to
  150. X     appear on multiple target lines.
  151. X
  152. XThere is a short story which goes with this offering.  Sometime early in
  153. X1988, someone (possibly Dan Grayson) posted copyrighted source for a
  154. X`make' to Usenet.  I used it and modified it somewhat, and then lost my
  155. Xhard disk.  Having found this program to be very useful, I set about
  156. Xrewriting it from my recollection of the source that I had seen.  I have
  157. Xasked Rahul (moderator of comp.binaries.ibm.pc) if he could trace the
  158. Xoriginal submitter, and have also posted a note to the net in c.b.i.p.d,
  159. Xtrying to locate this person.  So far, no trace has been found.  I
  160. Xreally would like to show this source to him(and hopefully have him
  161. Xagree that it is not the same as his).
  162. X
  163. XI have based my algorithms on this previous source code.  These are not
  164. Xcopyrightable, so I feel that I have not infringed upon anyone's rights.
  165. XAlso, I feel that I have acted in good faith trying to trace this
  166. Xperson.  I hope that recipients of this code feel the same.  I am
  167. Xreleasing this into the public domain.  You may do anything you wish
  168. Xwith it, even copyright it yourself and try to sell it as your own.
  169. XGood luck, and have fun.
  170. X
  171. X    -greg
  172. X
  173. XGreg Yachuk    Informix Software Inc., Menlo Park, CA    (415) 926-6300
  174. X{uunet,pyramid}!infmx!greggy    why yes, I DID choose that login myself
  175. END_OF_FILE
  176. if test 4707 -ne `wc -c <'README'`; then
  177.     echo shar: \"'README'\" unpacked with wrong size!
  178. fi
  179. # end of 'README'
  180. fi
  181. if test -f 'build.c' -a "${1}" != "-c" ; then 
  182.   echo shar: Will not clobber existing file \"'build.c'\"
  183. else
  184. echo shar: Extracting \"'build.c'\" \(4850 characters\)
  185. sed "s/^X//" >'build.c' <<'END_OF_FILE'
  186. X/*
  187. X * build.c    An imitation of the Unix MAKE facility
  188. X *
  189. X * 88-10-01 v1.0    created by greg yachuk, placed in the public domain
  190. X * 88-10-06 v1.1    changed prerequisite list handling
  191. X * 88-11-11 v1.2    fixed some bugs and added environment variables
  192. X * 89-07-12 v1.3    stop appending shell commands, and flush output
  193. X * 89-08-01 v1.4 AB    lots of new options and code
  194. X * 89-10-30 v1.5 greggy    -f -S -q options, took some changes from v1.4
  195. X *
  196. X */
  197. X
  198. X#include <stdio.h>
  199. X#include <string.h>
  200. X#ifdef    MSDOS
  201. X#include <stdlib.h>
  202. X#include <process.h>
  203. X#else
  204. X#include <sys/wait.h>
  205. X#endif
  206. X
  207. X#include "make.h"
  208. X#include "tstring.h"
  209. X#include "decl.h"
  210. X
  211. Xchar   *shell_cmds[] = {
  212. X#ifdef    MSDOS
  213. X    "dir", "type", "rem", "pause", "date", "time", "ren", "rename",
  214. X    "ver", "vol", "break", "verify", "mkdir", "md", "exit", "ctty",
  215. X    "echo", "if", "cls", "chdir", "cd", "rmdir", "rd", "copy", "del",
  216. X    "erase",
  217. X#endif
  218. X    NULL,
  219. X};
  220. X
  221. X/*
  222. X * build    - process the shell commands
  223. X */
  224. Xbuild(shellp)
  225. XREGISTER shellptr *shellp;
  226. X{
  227. X    REGISTER char **argv;    /* argified version of scmd */
  228. X    int     runst;        /* exec return status */
  229. X    char   *scmd;        /* command with symbols broken out */
  230. X    char   *tcmd;        /* copy of scmd for `tokenize' */
  231. X    char   *errnum;        /* error number in ascii */
  232. X    char   *errmsg;        /* error message */
  233. X#ifdef    MSDOS
  234. X    char  **shcp;        /* pointer to shell command list */
  235. X#endif
  236. X
  237. X    if (shellp == NULL || opts.query)
  238. X        return (0);
  239. X
  240. X    for (; *shellp != NULL;
  241. X         tfree(scmd), tfree(tcmd), tfree(argv), ++shellp)
  242. X    {
  243. X        /* breakout runtime symbols (e.g. $* $@ $<) */
  244. X        scmd = breakout((*shellp)->scmd);
  245. X
  246. X        if (!opts.silent && (opts.noexec || !(*shellp)->s_silent))
  247. X        {
  248. X            puts(scmd);
  249. X            fflush(stdout);
  250. X        }
  251. X
  252. X        /* make a copy because tokenize litters '\0's */
  253. X        tcmd = tstrcpy(scmd);
  254. X        argv = tokenize(tcmd);
  255. X
  256. X        /* look for $(MAKE) */
  257. X        if (equal(argv[0], opts.make))
  258. X        {
  259. X            /* call ourselves recursively */
  260. X            new_make(argv);
  261. X            continue;
  262. X        }
  263. X
  264. X        if (opts.noexec)
  265. X            continue;
  266. X
  267. X        /* any SHELL meta-characters MUST be handled by the shell */
  268. X        if (!(*shellp)->s_shell && strpbrk(scmd, SHELL_METAS))
  269. X            (*shellp)->s_shell = 1;
  270. X#ifdef    MSDOS
  271. X        /* likewise, check for COMMAND.COM builtin commands */
  272. X        shcp = shell_cmds;
  273. X        for (; !(*shellp)->s_shell && *shcp; ++shcp)
  274. X            (*shellp)->s_shell = equal(*shcp, argv[0]);
  275. X#endif
  276. X        /* run without COMMAND.COM if possible, 'cause it uses RAM */
  277. X        if ((*shellp)->s_shell)
  278. X            runst = system(scmd);
  279. X        else
  280. X            runst = spawnvp(P_WAIT, argv[0], argv);
  281. X
  282. X        if (runst == 0)
  283. X            continue;
  284. X
  285. X        /* uh-oh, an error */
  286. X        if (runst == -1)
  287. X            perror("make");
  288. X
  289. X        errnum = talloc(18);
  290. X#ifdef    MSDOS
  291. X        errnum = itoa(runst, errnum, 10);
  292. X#else
  293. X        sprintf(errnum, "%d", runst);
  294. X#endif
  295. X        errmsg = (opts.keepon) ? "\007*** Ignoring Error code "
  296. X            : "\007*** Error code ";
  297. X        errmsg = tstrcat(errmsg, errnum);
  298. X        terror(0, errmsg);
  299. X        tfree(errmsg);
  300. X        tfree(errnum);
  301. X
  302. X        if (opts.keepon)
  303. X            return (1);
  304. X
  305. X        if (!opts.ignore && !(*shellp)->s_ignore)
  306. X            exit(1);
  307. X    }
  308. X
  309. X    return (0);
  310. X}
  311. X
  312. X
  313. X/*
  314. X * new_make    - save current environment
  315. X *        - call make() recursively (actually main())
  316. X *        - clean up new environment
  317. X *        - restore environment
  318. X */
  319. Xnew_make(argv)
  320. Xchar  **argv;
  321. X{
  322. X    targptr thead, tnext, tsuffix;
  323. X    fileptr fhead, fnext;
  324. X    symptr  shead, snext;
  325. X    shellptr shhead, shnext;
  326. X    char  **ttlist;
  327. X    long    tnow;
  328. X    optnode topts;
  329. X    int     i;
  330. X
  331. X    /* save all the globals */
  332. X    tsuffix = suffix_targ;
  333. X    thead = target_list;
  334. X    fhead = file_list;
  335. X    shead = symbol_list;
  336. X    shhead = shell_list;
  337. X    ttlist = tlist;
  338. X    tnow = now;
  339. X    topts = opts;
  340. X
  341. X    /* count the arguments */
  342. X    for (i = 0; argv[i]; ++i)
  343. X        tunquote(argv[i]);
  344. X
  345. X    /* call ourselves recursively; this inherits flags */
  346. X    ++make_level;
  347. X    main(i, argv);
  348. X    --make_level;
  349. X
  350. X    /* we're back, so gotta clean up and dispose of a few things */
  351. X    while (target_list)
  352. X    {
  353. X        tnext = target_list->tnext;
  354. X        if (target_list->tpreq)
  355. X            tfree(target_list->tpreq);
  356. X        if (target_list->tshell)
  357. X            tfree(target_list->tshell);
  358. X        tfree(target_list);
  359. X        target_list = tnext;
  360. X    }
  361. X
  362. X    while (file_list)
  363. X    {
  364. X        fnext = file_list->fnext;
  365. X        tfree(file_list->fname);
  366. X        tfree(file_list);
  367. X        file_list = fnext;
  368. X    }
  369. X
  370. X    /* don't drop all symbols, just the new ones */
  371. X
  372. X    while (symbol_list != shead)
  373. X    {
  374. X        snext = symbol_list->snext;
  375. X        tfree(symbol_list->sname);
  376. X        tfree(symbol_list->svalue);
  377. X        tfree(symbol_list);
  378. X        symbol_list = snext;
  379. X    }
  380. X
  381. X    while (shell_list)
  382. X    {
  383. X        shnext = shell_list->slink;
  384. X        tfree(shell_list->scmd);
  385. X        tfree(shell_list);
  386. X        shell_list = shnext;
  387. X    }
  388. X
  389. X    /* restore our original globals */
  390. X    suffix_targ = tsuffix;
  391. X    target_list = thead;
  392. X    file_list = fhead;
  393. X    symbol_list = shead;
  394. X    shell_list = shhead;
  395. X    tlist = ttlist;
  396. X    now = tnow;
  397. X    opts = topts;
  398. X}
  399. X
  400. X
  401. X#ifndef    MSDOS
  402. Xint
  403. Xspawnvp(mode, path, args)
  404. Xint     mode;
  405. Xchar   *path;
  406. Xchar  **args;
  407. X{
  408. X    int     pid = 0;
  409. X    union wait waitword;
  410. X
  411. X    if (mode != P_OVERLAY)
  412. X        pid = fork();
  413. X
  414. X    if (pid == 0)
  415. X        execvp(path, args);
  416. X
  417. X    wait(&waitword);
  418. X    return (waitword.w_retcode);
  419. X}
  420. X#endif
  421. END_OF_FILE
  422. if test 4850 -ne `wc -c <'build.c'`; then
  423.     echo shar: \"'build.c'\" unpacked with wrong size!
  424. fi
  425. # end of 'build.c'
  426. fi
  427. if test -f 'make.c' -a "${1}" != "-c" ; then 
  428.   echo shar: Will not clobber existing file \"'make.c'\"
  429. else
  430. echo shar: Extracting \"'make.c'\" \(17680 characters\)
  431. sed "s/^X//" >'make.c' <<'END_OF_FILE'
  432. X/*
  433. X * make.c    An imitation of the Unix MAKE facility
  434. X *
  435. X * 88-10-01 v1.0    created by greg yachuk, placed in the public domain
  436. X * 88-10-06 v1.1    changed prerequisite list handling
  437. X * 88-11-11 v1.2    fixed some bugs and added environment variables
  438. X * 89-07-12 v1.3    stop appending shell commands, and flush output
  439. X * 89-08-01 v1.4 AB    lots of new options and code
  440. X * 89-10-30 v1.5 greggy    -f -S -q options, took some changes from v1.4
  441. X *
  442. X */
  443. X
  444. X#include <stdio.h>
  445. X#include <errno.h>
  446. X#include <fcntl.h>
  447. X#include <string.h>
  448. X#include <sys/types.h>
  449. X#include <sys/stat.h>
  450. X#ifdef    MSDOS
  451. X#include <stdlib.h>
  452. X#endif
  453. X
  454. X#include "make.h"
  455. X#include "tstring.h"
  456. X#include "decl.h"
  457. X
  458. X
  459. Xtargptr target_list = NULL;    /* list of target nodes */
  460. Xfileptr file_list = NULL;    /* list of file nodes */
  461. Xsymptr  symbol_list = NULL;    /* list of symbol nodes */
  462. Xshellptr shell_list = NULL;    /* list of shell nodes */
  463. X
  464. Xint     make_level = 0;        /* for counting new_make()'s */
  465. X
  466. Xtargptr first_targ = NULL;    /* first target, in case nothing explicit */
  467. Xtargptr suffix_targ = NULL;    /* .SUFFIXES target pointer */
  468. X
  469. Xchar  **tlist = NULL;        /* command line targets */
  470. Xchar  **flist = NULL;        /* command line make files */
  471. Xchar  **mlist = NULL;        /* command line macros */
  472. X
  473. Xint     tmax = 0;        /* max size of tlist */
  474. Xint     fmax = 0;        /* max size of flist */
  475. Xint     mmax = 0;        /* max size of mlist */
  476. X
  477. Xoptnode opts;            /* all the options */
  478. Xint     readdef = 1;        /* -r option */
  479. Xint     dispcount = 0;        /* used for -D option */
  480. X
  481. Xlong    now;            /* time at startup */
  482. Xchar   *makeflags;        /* value to update the MAKEFLAGS macro with */
  483. X
  484. X
  485. Xmain(argc, argv)
  486. Xint     argc;
  487. Xchar  **argv;
  488. X{
  489. X    REGISTER int i;
  490. X    REGISTER targptr targp;
  491. X    REGISTER int mk;
  492. X    symptr  symp;
  493. X    char   *envp;
  494. X    char  **envv;
  495. X
  496. X    /* initialize the various global lists */
  497. X
  498. X    opts.depend = 0;
  499. X    dispcount = 0;
  500. X
  501. X    target_list = NULL;
  502. X    file_list = NULL;
  503. X    shell_list = NULL;
  504. X    /* don't set symbol_list to NULL, or recursive makes won't work */
  505. X
  506. X    /* allocate space for command line targets, files and macros */
  507. X
  508. X    tlist = grow_list(NULL, &tmax);
  509. X    flist = grow_list(NULL, &fmax);
  510. X    mlist = grow_list(NULL, &mmax);
  511. X
  512. X    /* process MAKEFLAGS environment variable, first */
  513. X
  514. X    symp = get_symbol("MAKEFLAGS", 0);
  515. X    if (symp->svalue != NULL)
  516. X    {
  517. X        /* chop up the MAKEFLAGS and feed them to to make_args() */
  518. X
  519. X        envp = tstrcpy(symp->svalue);
  520. X        envv = tokenize(envp);
  521. X        for (i = 0; envv[i] != NULL; i++);
  522. X        make_args(i, envv);
  523. X
  524. X        /* free the vector of pointers, and the string itself, */
  525. X        /* since you cannot have macros, targets or makefiles  */
  526. X        /* in the MAKEFLAGS macro.                             */
  527. X
  528. X        tfree(envv);
  529. X        tfree(envp);
  530. X        tfree(makeflags);    /* ignore this, since we just read it */
  531. X    }
  532. X
  533. X    make_args(--argc, ++argv);    /* process command line options */
  534. X
  535. X    add_macro(makeflags, 0);/* update the MAKEFLAGS macro */
  536. X    tfree(makeflags);
  537. X
  538. X    /* add command line macros, so they DON'T get overridden */
  539. X
  540. X    for (i = 0; mlist[i] != NULL; i++)
  541. X        add_macro(mlist[i], 1);
  542. X
  543. X    tfree(mlist);        /* all done with macros */
  544. X
  545. X    if (opts.query)        /* -q never executes anything */
  546. X        opts.noexec = 1;
  547. X
  548. X    if (opts.noexec)
  549. X        opts.touch = 0;    /* -n never touches */
  550. X
  551. X    if (dispcount > 1)    /* display `default.mk' on -DD */
  552. X        opts.display = 1;
  553. X
  554. X    first_targ = NULL;    /* used in parse() */
  555. X
  556. X    if (readdef)        /* read in `default.mk' */
  557. X        parse(fopenp(MAKEINI, "r"));
  558. X
  559. X    if (dispcount > 0)    /* display makefile's on -D */
  560. X        opts.display = 1;
  561. X
  562. X    first_targ = NULL;    /* get first target in `makefile' */
  563. X
  564. X    /* parse the makefiles given on command line */
  565. X    for (i = 0; flist[i] != NULL; i++)
  566. X    {
  567. X        parse(equal(flist[i], "-") ? fdopen(dup(fileno(stdin)), "r")
  568. X              : fopen(flist[i], "r"));
  569. X    }
  570. X
  571. X    /* no makefiles specified, so use "makefile" or "Makefile" */
  572. X    if (i == 0)
  573. X    {
  574. X        if (parse(fopen("makefile", "r")) == 0)
  575. X        {
  576. X#ifndef    MSDOS
  577. X            parse(fopen("Makefile", "r"));
  578. X#endif
  579. X        }
  580. X    }
  581. X
  582. X    tfree(flist);        /* all done with makefile's */
  583. X
  584. X    /* find the current value of the $(MAKE) macro */
  585. X    symp = get_symbol("MAKE", 0);
  586. X    opts.make = (symp->svalue == NULL) ? "make" : symp->svalue;
  587. X
  588. X    if ((targp = get_target(".INIT")) != NULL)
  589. X        build(targp->tshell);    /* process the .INIT rule */
  590. X
  591. X    mk = 0;
  592. X
  593. X    for (i = 0; tlist[i] != NULL; i++)
  594. X    {
  595. X        /* process command line arguments */
  596. X        mk |= (make(tlist[i], 1) > 0) ? 1 : 0;
  597. X    }
  598. X
  599. X    tfree(tlist);        /* all done with targets */
  600. X
  601. X    /* if no targets specified, make the first one */
  602. X    if (i == 0 && first_targ)
  603. X        mk |= (make(first_targ->tfile->fname, 1) > 0) ? 1 : 0;
  604. X
  605. X    if ((targp = get_target(".DONE")) != NULL)
  606. X        build(targp->tshell);    /* process the .DONE rule */
  607. X
  608. X    return (mk & opts.query);    /* not exit(); see new_make() */
  609. X}
  610. X
  611. X
  612. X/*
  613. X * make_args    - process the command line arguments
  614. X */
  615. Xmake_args(argc, argv)
  616. Xint     argc;
  617. Xchar  **argv;
  618. X{
  619. X    REGISTER int tlen;
  620. X    REGISTER int flen;
  621. X    REGISTER int mlen;
  622. X    REGISTER int no_k = 0;    /* override the -k option */
  623. X    char   *tmf;
  624. X    int     addflag;
  625. X
  626. X    now = time(NULL);    /* get current date & time */
  627. X
  628. X    makeflags = tstrcpy("MAKEFLAGS+=");
  629. X
  630. X    tlen = flen = mlen = 0;
  631. X
  632. X    for (; argc != 0; ++argv, --argc)
  633. X    {
  634. X        if (**argv != '-')
  635. X        {
  636. X            /* doesn't start with '-'; must be macro or target */
  637. X
  638. X            if (strchr(*argv, '='))
  639. X            {    /* store as a macro */
  640. X                if (mlen == mmax)
  641. X                    mlist = grow_list(mlist, &mmax);
  642. X                mlist[mlen++] = *argv;
  643. X            }
  644. X            else
  645. X            {    /* store as a target */
  646. X                if (tlen == tmax)
  647. X                    tlist = grow_list(tlist, &tmax);
  648. X                tlist[tlen++] = *argv;
  649. X            }
  650. X            continue;
  651. X        }
  652. X
  653. X        /* must be an option */
  654. X
  655. X        tmf = tstrcat(makeflags, *argv);
  656. X
  657. X        while (*argv && *++*argv)
  658. X        {
  659. X            addflag = 1;    /* add to MAKEFLAGS */
  660. X            switch (**argv)
  661. X            {
  662. X            case 'd':    /* show dependencies */
  663. X                addflag = 0;    /* don't add to MAKEFLAGS */
  664. X                opts.depend++;
  665. X                break;
  666. X
  667. X            case 'D':    /* display makefiles */
  668. X                dispcount++;
  669. X                break;
  670. X
  671. X            case 'e':    /* don't override environment */
  672. X                opts.envirn = 1;
  673. X                break;
  674. X
  675. X            case 'f':    /* new makefile name */
  676. X                addflag = 0;    /* don't add to MAKEFLAGS */
  677. X                if (argc < 2)
  678. X                    usage();
  679. X                if (flen == fmax)
  680. X                    flist = grow_list(flist, &fmax);
  681. X                ++argv, --argc;
  682. X                flist[flen++] = *argv;
  683. X
  684. X                *argv = NULL;
  685. X                break;
  686. X
  687. X            case 'i':    /* ignore errors */
  688. X                opts.ignore = 1;
  689. X                break;
  690. X
  691. X            case 'k':    /* give up on current target on error */
  692. X                opts.keepon = 1;
  693. X                break;
  694. X
  695. X            case 'n':    /* don't execute commands */
  696. X                opts.noexec = 1;
  697. X                break;
  698. X
  699. X            case 'q':    /* question mode */
  700. X                opts.query = 1;
  701. X                break;
  702. X
  703. X            case 'r':    /* don't read default.mk */
  704. X                readdef = 0;
  705. X                break;
  706. X
  707. X            case 's':    /* don't echo commands */
  708. X                opts.silent = 1;
  709. X                break;
  710. X
  711. X            case 'S':    /* Undo -k option */
  712. X                no_k = 1;
  713. X                break;
  714. X
  715. X            case 't':    /* touch files, don't build */
  716. X                opts.touch = 1;
  717. X                break;
  718. X
  719. X            default:
  720. X                usage();    /* never returns */
  721. X            }
  722. X        }
  723. X
  724. X        if (addflag)
  725. X        {
  726. X            tfree(makeflags);
  727. X            makeflags = tstrcat(tmf, " ");
  728. X        }
  729. X
  730. X        tfree(tmf);
  731. X    }
  732. X
  733. X    /* terminate all lists with a NULL pointer */
  734. X
  735. X    tlist[tlen] = NULL;
  736. X    flist[flen] = NULL;
  737. X    mlist[mlen] = NULL;
  738. X
  739. X    /* check for -S over-riding -k option */
  740. X    if (no_k)
  741. X        opts.keepon = 0;
  742. X
  743. X    /* let the caller update the makeflags macro */
  744. X}
  745. X
  746. X
  747. X/*
  748. X * grow_list    - expand the list of pointers by a factor of two
  749. X */
  750. Xchar  **
  751. Xgrow_list(list, len)
  752. Xchar  **list;
  753. Xint    *len;
  754. X{
  755. X    REGISTER int l;
  756. X
  757. X    /* if list is NULL, start off with a default list */
  758. X
  759. X    if (list == NULL)
  760. X        list = (char **) talloc(((l = 1) + 1) * sizeof(char *));
  761. X    else
  762. X    {
  763. X        l = *len;    /* get current length */
  764. X
  765. X        list = (char **) trealloc((char *) list,
  766. X                      ((l <<= 1) + 1) * sizeof(char *));
  767. X    }
  768. X
  769. X    if (list == NULL)
  770. X        terror(1, "too many options");
  771. X
  772. X    /* if we are initially allocating it, set first pointer to NULL */
  773. X
  774. X    if (l == 1)
  775. X        *list = NULL;
  776. X
  777. X    *len = l;        /* update current length */
  778. X    return (list);
  779. X}
  780. X
  781. X
  782. X/*
  783. X * fopenp    - open file in current directory or along PATH
  784. X */
  785. XFILE   *
  786. Xfopenp(fname, type)
  787. Xchar   *fname;
  788. Xchar   *type;
  789. X{
  790. X    REGISTER int len;
  791. X    REGISTER char *fpath;
  792. X    FILE   *fd;
  793. X    char   *path;
  794. X    char   *tp;
  795. X
  796. X    /* try to open file relative to current directory */
  797. X    if ((fd = fopen(fname, type)) != NULL)
  798. X        return (fd);
  799. X#ifndef    MSDOS
  800. X    /* didn't work, try home directory */
  801. X    if ((path = getenv("HOME")) != NULL)
  802. X    {
  803. X        fpath = talloc(strlen(path) + strlen(fname) + 2);
  804. X
  805. X        strcpy(fpath, path);
  806. X        len = strlen(fpath) - 1;
  807. X
  808. X        /* make sure there is a separator between path and filename */
  809. X
  810. X        if (!strchr(FILE_SEPARATOR, fpath[len]))
  811. X            fpath[++len] = '/';
  812. X
  813. X        strcpy(&fpath[len + 1], fname);    /* attach the filename */
  814. X        fd = fopen(fpath, type);
  815. X        tfree(fpath);
  816. X
  817. X        if (fd != NULL)
  818. X            return (fd);
  819. X    }
  820. X#endif
  821. X    /* didn't work, search along path */
  822. X
  823. X    if ((path = getenv("PATH")) == NULL)
  824. X        return (NULL);
  825. X
  826. X    path = tstrcpy(path);    /* allocate string and copy */
  827. X    fpath = talloc(strlen(path) + strlen(fname) + 2);
  828. X
  829. X    /* look for tokens separated by semi-colons (;) or colons (:) */
  830. X
  831. X    tp = token(path, PATH_SEPARATOR, NULL);
  832. X    while (tp != NULL)
  833. X    {
  834. X        strcpy(fpath, tp);
  835. X        len = strlen(fpath) - 1;
  836. X
  837. X        /* make sure there is a separator between path and filename */
  838. X
  839. X        if (!strchr(FILE_SEPARATOR, fpath[len]))
  840. X            fpath[++len] = '/';
  841. X
  842. X        strcpy(&fpath[len + 1], fname);    /* attach the filename */
  843. X        if ((fd = fopen(fpath, type)) != NULL)
  844. X            break;
  845. X
  846. X        tp = token(NULL, PATH_SEPARATOR, NULL);
  847. X    }
  848. X
  849. X    tfree(path);
  850. X    tfree(fpath);
  851. X
  852. X    return (fd);
  853. X}
  854. X
  855. X
  856. X/*
  857. X * make        - guts of the make command
  858. X *        - make all pre-requisites, and if necessary, build target
  859. X *
  860. X *    returns    -1 target was already up to date w.r.t. pre-requisites
  861. X *         0 target has not been built
  862. X *         1 target is now built (and up to date)
  863. X */
  864. Xmake(targname, worry)
  865. Xchar   *targname;
  866. Xint     worry;            /* if set, it is an error to NOT build this */
  867. X{
  868. X    REGISTER targptr targp;
  869. X    REGISTER fileptr *preqp;
  870. X    REGISTER int mk;
  871. X    fileptr filep;
  872. X    long    targtime;
  873. X    long    preqtime;
  874. X
  875. X    mk = 0;
  876. X
  877. X    /* if recorded time of file is not default, we've already built it */
  878. X    filep = get_file(targname);
  879. X    if (filep && filep->ftime != MAXNEGTIME)
  880. X        return (1);
  881. X
  882. X    targp = get_target(targname);    /* find the target node */
  883. X    if (targp == NULL)
  884. X        return (default_rule(targname, NULL, worry, 0));
  885. X
  886. X    /* keep actual time of current target */
  887. X    targtime = file_time(targname, 0);
  888. X
  889. X    /* must build non-existant files, even with no pre-requisites */
  890. X    preqtime = MAXNEGTIME + 1;
  891. X
  892. X    /* make all pre-requisites */
  893. X    preqp = targp->tpreq;
  894. X    while (preqp && *preqp)
  895. X    {
  896. X        mk |= make((*preqp)->fname, worry);
  897. X
  898. X        /* keep track of newest pre-requisite */
  899. X        if (preqtime < (*preqp)->ftime)
  900. X            preqtime = (*preqp)->ftime;
  901. X
  902. X        /* display as necessary */
  903. X        if (opts.depend > 1 ||
  904. X            (opts.depend && (*preqp)->ftime > targtime))
  905. X        {
  906. X            display_prereq(targname, targtime, (*preqp)->fname,
  907. X                       (*preqp)->ftime);
  908. X        }
  909. X
  910. X        ++preqp;
  911. X    }
  912. X
  913. X    if (targp->tshell == NULL)    /* try default rules anyway */
  914. X    {
  915. X        if (default_rule(targname, targp, 0, preqtime > targtime))
  916. X            return (1);
  917. X        return (mk);
  918. X    }
  919. X    else if (preqtime > targtime)
  920. X    {
  921. X        if (opts.touch)    /* won't be set when `noexec' */
  922. X            touch_file(targname);
  923. X        else
  924. X        {
  925. X            add_metas("", "", targname);
  926. X            if (build(targp->tshell))
  927. X                return (0);
  928. X        }
  929. X
  930. X        targp->tfile->ftime = (opts.noexec) ? now
  931. X            : file_time(targname, 1);
  932. X        return (1);
  933. X    }
  934. X
  935. X    targp->tfile->ftime = targtime;
  936. X
  937. X    return (mk);
  938. X}
  939. X
  940. X
  941. X/*
  942. X * default_rule    - try the .SUFFIXES when we don't have an explicit target
  943. X *        - if `worry' is set, it is an ERROR to NOT build this target
  944. X *        - `mustbuild' is set if make() has out-of-date prereq's
  945. X *           but no explicit shell rules
  946. X */
  947. Xdefault_rule(targname, targetp, worry, mustbuild)
  948. Xchar   *targname;
  949. Xtargptr targetp;
  950. Xint     worry;
  951. Xint     mustbuild;
  952. X{
  953. X    REGISTER targptr targp;
  954. X    REGISTER fileptr *preqp;
  955. X    fileptr filep;
  956. X    char   *ext;
  957. X    char   *basename;
  958. X    char   *preqname;
  959. X    long    targtime;
  960. X    long    preqtime;
  961. X    int     built;
  962. X    char    suffrule[80];
  963. X
  964. X    ext = strrchr(targname, '.');    /* find the extension */
  965. X    if (ext == NULL)
  966. X        ext = targname + strlen(targname);
  967. X
  968. X    basename = tstrncpy(targname, ext - targname);    /* find the base name */
  969. X
  970. X    targtime = file_time(targname, 0);
  971. X
  972. X    /* suffix_targ is used to (slightly) speed up this function */
  973. X    preqp = suffix_targ ? suffix_targ->tpreq : NULL;
  974. X    built = 0;
  975. X
  976. X    while (preqp && *preqp && !built)
  977. X    {
  978. X        /* look for a default rule from SUFFIX to `ext' */
  979. X        strcat(strcpy(suffrule, (*preqp)->fname), ext);
  980. X        targp = get_target(suffrule);    /* e.g. `.c.o' */
  981. X
  982. X        if (targp != NULL)
  983. X        {
  984. X            /* found a rule; see if file exists */
  985. X            preqname = get_preqname(targetp, (*preqp)->fname,
  986. X                        basename);
  987. X            preqtime = file_time(preqname, 0);
  988. X
  989. X            /*
  990. X             * don't bother recursive makes unless necessary e.g.
  991. X             * we have .c.o and .l.c, but also .l.o! we want to
  992. X             * use .l.o if a .c file does not exist
  993. X             */
  994. X            if (preqtime != MAXNEGTIME || mustbuild)
  995. X                built = make(preqname, 0);
  996. X
  997. X            /* check if pre-req file exists and is newer */
  998. X            preqtime = file_time(preqname, 0);
  999. X            if (preqtime > targtime || (mustbuild && built))
  1000. X            {
  1001. X                if (opts.depend)
  1002. X                {
  1003. X                    display_prereq(targname, targtime,
  1004. X                               preqname, preqtime);
  1005. X                }
  1006. X
  1007. X                if (opts.touch)    /* won't be set when `noexec' */
  1008. X                    touch_file(targname);
  1009. X                else
  1010. X                {
  1011. X                    add_metas(basename, preqname, targname);
  1012. X                    if (build(targp->tshell))
  1013. X                        return (0);
  1014. X                }
  1015. X                built = 1;
  1016. X            }
  1017. X            else if (opts.depend > 1 && preqtime != MAXNEGTIME)
  1018. X            {
  1019. X                display_prereq(targname, targtime,
  1020. X                           preqname, preqtime);
  1021. X            }
  1022. X
  1023. X            tfree(preqname);
  1024. X        }
  1025. X
  1026. X        ++preqp;    /* try next .SUFFIXES rule */
  1027. X    }
  1028. X
  1029. X    if (!built)
  1030. X    {
  1031. X        /* didn't find anything; try the default rule */
  1032. X        targp = get_target(".DEFAULT");
  1033. X        if (targp != NULL)
  1034. X        {
  1035. X            add_metas(basename, "", targname);
  1036. X            if (build(targp->tshell))
  1037. X                return (0);
  1038. X            built = 1;
  1039. X        }
  1040. X        else if (targtime == MAXNEGTIME && worry)
  1041. X            terror(1, tstrcat("Don't know how to make ", targname));
  1042. X    }
  1043. X
  1044. X    tfree(basename);
  1045. X
  1046. X    /* record the current file time */
  1047. X    if ((filep = get_file(targname)) != NULL)
  1048. X    {
  1049. X        filep->ftime = (built == 1 && opts.noexec) ? now
  1050. X            : file_time(targname, 1);
  1051. X    }
  1052. X
  1053. X    return (built ? built : ((targtime == MAXNEGTIME) ? 0 : 1));
  1054. X}
  1055. X
  1056. X
  1057. X/*
  1058. X * get_preqname - find prerequisite name from target and prerequisite suffix
  1059. X */
  1060. Xchar   *
  1061. Xget_preqname(targp, suffix, basename)
  1062. Xtargptr targp;
  1063. Xchar   *suffix;
  1064. Xchar   *basename;
  1065. X{
  1066. X    fileptr *preqp;
  1067. X    char   *preqf;
  1068. X    char   *basef;
  1069. X    int     i;
  1070. X
  1071. X    if (targp != NULL)
  1072. X    {
  1073. X        /* strip the directory name from the basename */
  1074. X        basef = tsplit(basename, FILE_SEPARATOR, NULL);
  1075. X
  1076. X        /* look through prerequisite list for file with right name */
  1077. X        for (preqp = targp->tpreq; preqp && *preqp; ++preqp)
  1078. X        {
  1079. X            /* split the pre-requisite into dir and filenames */
  1080. X            preqf = tsplit((*preqp)->fname, FILE_SEPARATOR, NULL);
  1081. X
  1082. X            /* see if the filename part matches the target */
  1083. X            for (i = 0; preqf[i] != '\0'; i++)
  1084. X            {
  1085. X                if (preqf[i] != basef[i])
  1086. X                    break;
  1087. X            }
  1088. X
  1089. X            /* if we differed only on the suffix, we're okay */
  1090. X            if (strcmp(preqf + i, suffix) == 0)
  1091. X                return (tstrcpy((*preqp)->fname));
  1092. X        }
  1093. X#ifdef    ALL_PREQS
  1094. X        /* didn't find a matching basename + suffix in the preq-list. */
  1095. X        /* look through prerequisite list for file with right suffix. */
  1096. X        for (preqp = targp->tpreq; preqp && *preqp; ++preqp)
  1097. X        {
  1098. X            preqf = strrchr((*preqp)->fname, '.');
  1099. X            if (preqf == NULL)
  1100. X                continue;
  1101. X
  1102. X            /* take the first file which has right suffix */
  1103. X            if (strcmp(suffix, preqf) == 0)
  1104. X                return (tstrcpy((*preqp)->fname));
  1105. X        }
  1106. X#endif                /* ALL_PREQS */
  1107. X    }
  1108. X
  1109. X    /* didn't find one, so try forming one using basename + suffix */
  1110. X
  1111. X    return (tstrcat(basename, suffix));
  1112. X}
  1113. X
  1114. X
  1115. X/*
  1116. X * add_metas    - add symbols for $*, $< and $@
  1117. X */
  1118. Xadd_metas(basename, preqname, targname)
  1119. Xchar   *basename;
  1120. Xchar   *preqname;
  1121. Xchar   *targname;
  1122. X{
  1123. X    /* $* is the basename */
  1124. X    add_symbol("*", basename, 0);
  1125. X    split_meta("*", basename);
  1126. X
  1127. X    add_symbol("<", preqname, 0);
  1128. X    split_meta("<", preqname);
  1129. X
  1130. X    add_symbol("@", targname, 0);
  1131. X    split_meta("@", targname);
  1132. X}
  1133. X
  1134. X
  1135. X/*
  1136. X * split_meta -    split a metasymbol into Directory and File parts
  1137. X */
  1138. Xsplit_meta(sym, name)
  1139. Xchar   *sym;
  1140. Xchar   *name;
  1141. X{
  1142. X    char   *dname;
  1143. X    char   *dsym;
  1144. X    char   *fsym;
  1145. X
  1146. X    /* construct the macro names (e.g. $(*D), $(@F)) */
  1147. X    dsym = tstrcat(sym, "D");
  1148. X    fsym = tstrcat(sym, "F");
  1149. X
  1150. X    add_symbol(fsym, tsplit(name, FILE_SEPARATOR, &dname), 0);
  1151. X
  1152. X    if (dname == NULL)
  1153. X        add_symbol(dsym, ".", 0);
  1154. X    else
  1155. X    {
  1156. X        add_symbol(dsym, dname, 0);
  1157. X        tfree(dname);
  1158. X    }
  1159. X
  1160. X    tfree(dsym);
  1161. X    tfree(fsym);
  1162. X}
  1163. X
  1164. X
  1165. X/*
  1166. X * touch_file    - set the MODIFICATION time of the file to NOW
  1167. X */
  1168. Xtouch_file(targname)
  1169. Xchar   *targname;
  1170. X{
  1171. X    REGISTER int handle;
  1172. X
  1173. X#ifndef    MSDOS
  1174. X    time_t  timep[2];
  1175. X
  1176. X    time(&timep[0]);
  1177. X    timep[1] = timep[0];
  1178. X    handle = utime(targname, timep);
  1179. X#else
  1180. X    handle = utime(targname, NULL);
  1181. X#endif
  1182. X    fputs("touch ", stdout);
  1183. X    puts(targname);
  1184. X
  1185. X    if (handle == 0)
  1186. X        return;
  1187. X
  1188. X    /* create the file, if it did not exist */
  1189. X    if (errno == ENOENT)
  1190. X    {
  1191. X        handle = open(targname, O_CREAT | O_TRUNC, S_IWRITE);
  1192. X        if (handle != -1)
  1193. X        {
  1194. X            close(handle);
  1195. X            return;
  1196. X        }
  1197. X    }
  1198. X
  1199. X    perror("touch");
  1200. X    exit(1);
  1201. X}
  1202. X
  1203. Xdisplay_prereq(targname, targtime, preqname, preqtime)
  1204. Xchar   *targname;
  1205. Xlong    targtime;
  1206. Xchar   *preqname;
  1207. Xlong    preqtime;
  1208. X{
  1209. X#ifdef    MSDOS
  1210. X    char    chtime[10];
  1211. X
  1212. X    fputs(targname, stdout);
  1213. X    fputs(" (", stdout);
  1214. X    fputs(ltoa(targtime, chtime, 16), stdout);
  1215. X    fputs((targtime <= preqtime) ? ") older than " : ") newer than ", stdout);
  1216. X    fputs(preqname, stdout);
  1217. X    fputs(" (", stdout);
  1218. X    fputs(ltoa(preqtime, chtime, 16), stdout);
  1219. X    puts(")");
  1220. X#else
  1221. X    printf("%s (%08lx) %s than %s (%08lx)\n",
  1222. X           targname, targtime,
  1223. X           (targtime < preqtime) ? "older" : "newer",
  1224. X           preqname, preqtime);
  1225. X#endif
  1226. X}
  1227. X
  1228. X
  1229. Xlong
  1230. Xfile_time(fname, built)
  1231. Xchar   *fname;
  1232. Xint     built;
  1233. X{
  1234. X    struct stat sbuf;
  1235. X
  1236. X    /*
  1237. X     * if the file is supposedly built, but still does not exists, just
  1238. X     * fake it by returning the current time.
  1239. X     */
  1240. X    if (stat(fname, &sbuf) != 0)
  1241. X        return (built ? now : MAXNEGTIME);
  1242. X    return (sbuf.st_mtime);
  1243. X}
  1244. X
  1245. X
  1246. Xusage()
  1247. X{
  1248. X    puts("make [-f filename] [-dDiknqrsSt] [target ...] [macro=value ...]");
  1249. X    exit(1);
  1250. X}
  1251. END_OF_FILE
  1252. if test 17680 -ne `wc -c <'make.c'`; then
  1253.     echo shar: \"'make.c'\" unpacked with wrong size!
  1254. fi
  1255. # end of 'make.c'
  1256. fi
  1257. if test -f 'tstring.c' -a "${1}" != "-c" ; then 
  1258.   echo shar: Will not clobber existing file \"'tstring.c'\"
  1259. else
  1260. echo shar: Extracting \"'tstring.c'\" \(5471 characters\)
  1261. sed "s/^X//" >'tstring.c' <<'END_OF_FILE'
  1262. X/*
  1263. X * tstring.c
  1264. X *
  1265. X * 88-10-01 v1.0    created by greg yachuk, placed in the public domain
  1266. X * 88-10-06 v1.1    changed prerequisite list handling
  1267. X * 88-11-11 v1.2    fixed some bugs and added environment variables
  1268. X * 89-07-12 v1.3    stop appending shell commands, and flush output
  1269. X * 89-08-01 v1.4 AB    lots of new options and code
  1270. X * 89-10-30 v1.5 greggy    -f -S -q options, took some changes from v1.4
  1271. X *
  1272. X */
  1273. X#include <stdio.h>
  1274. X#include <ctype.h>
  1275. X#include <malloc.h>
  1276. X#include <string.h>
  1277. X
  1278. X#include "tstring.h"
  1279. X
  1280. X
  1281. Xchar   *
  1282. Xtalloc(n)
  1283. Xint     n;
  1284. X{
  1285. X    register char *s;
  1286. X
  1287. X    s = malloc(n);
  1288. X    if (s == NULL)
  1289. X        terror(1, "no free memory");
  1290. X    return (s);
  1291. X}
  1292. X
  1293. X
  1294. Xchar   *
  1295. Xtrealloc(s, n)
  1296. Xregister char *s;
  1297. Xint     n;
  1298. X{
  1299. X    s = realloc(s, n);
  1300. X    if (s == NULL)
  1301. X        talloc(n);    /* force an error */
  1302. X    return (s);
  1303. X}
  1304. X
  1305. X
  1306. Xchar   *
  1307. Xtstrncpy(s, n)
  1308. Xregister char *s;
  1309. Xint     n;
  1310. X{
  1311. X    s = strncpy(talloc(n + 1), s, n);
  1312. X    s[n] = '\0';
  1313. X    return (s);
  1314. X}
  1315. X
  1316. X
  1317. Xterror(n, s)
  1318. Xint     n;
  1319. Xchar   *s;
  1320. X{
  1321. X    fputs("Make: ", stderr);
  1322. X    fputs(s, stderr);
  1323. X    putc('\n', stderr);
  1324. X    if (n)
  1325. X        exit(n);
  1326. X}
  1327. X
  1328. X
  1329. X/*
  1330. X * tstrspan -    move to the end of a quoted string, ignoring escaped quotes
  1331. X */
  1332. Xchar   *
  1333. Xtstrspan(str)
  1334. Xchar   *str;
  1335. X{
  1336. X    char    quote;
  1337. X
  1338. X    if (*str != '\'' && *str != '"')
  1339. X        return (str + 1);
  1340. X
  1341. X    quote = *str++;
  1342. X
  1343. X    while (*str && *str != quote)
  1344. X    {
  1345. X        /* check for escaped quote */
  1346. X        if (*str == '\\' && str[1] == quote)
  1347. X            ++str;
  1348. X        ++str;
  1349. X    }
  1350. X
  1351. X    return (str);
  1352. X}
  1353. X
  1354. X
  1355. X/*
  1356. X * tunquote -    remove quotes from a string
  1357. X */
  1358. Xchar   *
  1359. Xtunquote(str)
  1360. Xchar   *str;
  1361. X{
  1362. X    char   *s;
  1363. X    char   *d;
  1364. X
  1365. X    d = s = str;
  1366. X
  1367. X    while (*s)
  1368. X    {
  1369. X        while (*s && *s == '"')
  1370. X            ++s;
  1371. X
  1372. X        while (*s && *s != '"')
  1373. X            *d++ = *s++;
  1374. X    }
  1375. X
  1376. X    *d = '\0';
  1377. X
  1378. X    return (str);
  1379. X}
  1380. X
  1381. X
  1382. X/*
  1383. X * tsplit -    split a string into two components, normally a directory
  1384. X *        path and a filename.  If a pointer to a directory is
  1385. X *        supplied, a string is allocated to contain the directory.
  1386. X *        The filename is returned as a pointer into the supplied
  1387. X *        string.
  1388. X */
  1389. Xchar   *
  1390. Xtsplit(s, seps, dp)
  1391. Xchar   *s;
  1392. Xchar   *seps;
  1393. Xchar  **dp;
  1394. X{
  1395. X    char   *d;        /* directory portion */
  1396. X    char   *f;        /* file portion */
  1397. X
  1398. X    d = s;
  1399. X
  1400. X    /* find the final separator */
  1401. X    while ((f = strpbrk(d, seps)) != NULL)
  1402. X        d = f + 1;
  1403. X
  1404. X    /* back up to final component */
  1405. X    f = d;
  1406. X
  1407. X    /* if we are still at the beginning, there was no Directory */
  1408. X    if (d == s || dp == NULL)
  1409. X        d = NULL;
  1410. X    else
  1411. X    {
  1412. X        int     len;
  1413. X
  1414. X        /*
  1415. X         * by the time we get here, d points to the final separator
  1416. X         * char.  we can substitute a NULL for this sep-char.  Thus,
  1417. X         * we don't need to add 1 in the following length
  1418. X         * calculation.
  1419. X         */
  1420. X        len = d - s;
  1421. X
  1422. X        d = talloc(len);
  1423. X        d[--len] = '\0';
  1424. X        while (--len >= 0)
  1425. X            d[len] = s[len];
  1426. X    }
  1427. X
  1428. X    if (dp != NULL)
  1429. X        *dp = d;
  1430. X
  1431. X    return (f);
  1432. X}
  1433. X
  1434. X
  1435. X/*
  1436. X * token    - take an input string and return a token each call
  1437. X *        - default token delimiter characters are `isspace()'
  1438. X *        - separator chars are in addition to `isspace()'
  1439. X *        - text between quotes (" and ') is a single token
  1440. X *        - if requested, the separator char is returned
  1441. X *
  1442. X *    called as    s = token(string, seps, &schar);
  1443. X *        or    s = token(string, NULL, NULL);
  1444. X *
  1445. X *    followed by    s = token(NULL, seps, NULL);
  1446. X *        or    s = token(NULL, NULL, &schar);
  1447. X *
  1448. X *    returns NULL when no more tokens are available
  1449. X */
  1450. Xchar   *
  1451. Xtoken(s, sep, schar)
  1452. Xchar   *s;
  1453. Xchar   *sep;
  1454. Xchar   *schar;
  1455. X{
  1456. X    static char *olds = NULL;
  1457. X
  1458. X    if (s)
  1459. X        olds = s;    /* we are starting all over again */
  1460. X
  1461. X    if (schar)
  1462. X        *schar = '\0';
  1463. X
  1464. X    if (!olds || !*olds)
  1465. X        return (NULL);    /* no tokens left */
  1466. X
  1467. X    while (isspace(*olds) || (sep && strchr(sep, *olds)))
  1468. X        ++olds;        /* skip leading spaces and sep's */
  1469. X
  1470. X    if (*olds == NULL)
  1471. X        return (NULL);    /* remainder is all separator's */
  1472. X
  1473. X    s = olds;
  1474. X
  1475. X    while (*olds)
  1476. X    {
  1477. X        if (isspace(*olds) || (sep && strchr(sep, *olds)))
  1478. X        {
  1479. X            if (schar)
  1480. X                *schar = *olds;
  1481. X            *olds++ = '\0';    /* delimit the token */
  1482. X            return (s);
  1483. X        }
  1484. X        else if (*olds == '"' || *olds == '\'')
  1485. X        {
  1486. X            olds = tstrspan(olds);
  1487. X            if (*olds != '\0')
  1488. X                ++olds;    /* didn't hit eos, so skip quote */
  1489. X        }
  1490. X        else
  1491. X            ++olds;    /* otherwise, pass over char */
  1492. X    }
  1493. X
  1494. X    olds = NULL;
  1495. X    return (s);        /* return last token */
  1496. X}
  1497. X
  1498. X
  1499. X/*
  1500. X * tokenize    - chop a string up into an array of (char *)'s
  1501. X */
  1502. Xchar  **
  1503. Xtokenize(input)
  1504. Xchar   *input;
  1505. X{
  1506. X    char  **argv;
  1507. X    register int argc = 0;
  1508. X    register int alen;
  1509. X
  1510. X    alen = 20;        /* good initial guess */
  1511. X    argv = (char **) talloc((alen + 1) * sizeof(char *));
  1512. X
  1513. X    input = token(input, NULL, NULL);    /* use default separators */
  1514. X    while (input)
  1515. X    {
  1516. X        if (alen == argc)
  1517. X            argv = (char **) trealloc((char *) argv,
  1518. X                         (alen <<= 1) * sizeof(char *));
  1519. X        argv[argc++] = input;
  1520. X        input = token(NULL, NULL, NULL);
  1521. X    }
  1522. X
  1523. X    argv[argc] = NULL;    /* mark end of array */
  1524. X
  1525. X    return (argv);
  1526. X}
  1527. X
  1528. X
  1529. X/*
  1530. X * tgets    - read input, swallowing escaped newlines as necessary
  1531. X */
  1532. Xchar   *
  1533. Xtgets(fd)
  1534. XFILE   *fd;
  1535. X{
  1536. X    static char *input = NULL;
  1537. X    static int inlen = 0;
  1538. X    register char *ep;
  1539. X    int     len;
  1540. X
  1541. X    if (inlen == 0)
  1542. X        input = talloc(inlen = 162);
  1543. X
  1544. X    input[inlen - 2] = '\n';
  1545. X    ep = input - 1;
  1546. X    while ((fgets(input, inlen, fd)) != NULL)
  1547. X    {
  1548. X        for (;;)
  1549. X        {
  1550. X            while (input[inlen - 2] != '\n' && input[inlen - 2] != '\0')
  1551. X            {
  1552. X                len = inlen;
  1553. X                input = trealloc(input, inlen <<= 1);
  1554. X                ep = &input[len - 2];
  1555. X                input[inlen - 2] = '\n';
  1556. X                fgets(ep + 1, len + 1, fd);
  1557. X            }
  1558. X
  1559. X            while (*++ep);
  1560. X            *--ep = '\0';
  1561. X            do
  1562. X            {
  1563. X                --ep;
  1564. X            } while (ep >= input && isspace(*ep));
  1565. X
  1566. X            if (ep > input && *ep == '\\' && *--ep != '\\')
  1567. X                fgets(ep + 1, inlen - (ep - input) - 1, fd);
  1568. X            else
  1569. X                break;
  1570. X        }
  1571. X
  1572. X        return (input);
  1573. X    }
  1574. X
  1575. X    inlen = 0;
  1576. X    tfree(input);
  1577. X    input = NULL;
  1578. X
  1579. X    return (NULL);
  1580. X}
  1581. END_OF_FILE
  1582. if test 5471 -ne `wc -c <'tstring.c'`; then
  1583.     echo shar: \"'tstring.c'\" unpacked with wrong size!
  1584. fi
  1585. # end of 'tstring.c'
  1586. fi
  1587. echo shar: End of archive 1 \(of 3\).
  1588. cp /dev/null ark1isdone
  1589. MISSING=""
  1590. for I in 1 2 3 ; do
  1591.     if test ! -f ark${I}isdone ; then
  1592.     MISSING="${MISSING} ${I}"
  1593.     fi
  1594. done
  1595. if test "${MISSING}" = "" ; then
  1596.     echo You have unpacked all 3 archives.
  1597.     rm -f ark[1-9]isdone
  1598. else
  1599.     echo You still need to unpack the following archives:
  1600.     echo "        " ${MISSING}
  1601. fi
  1602. ##  End of shell archive.
  1603. exit 0
  1604.  
  1605.